﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Castle.Core.Configuration;
using Wolfbyte.Core.Mvc.Routing;
using System.Web;

namespace Wolfbyte.Core.Mvc.Facilities
{
    internal class MvcSupportFacilityConfig : MvcSupportConfiguration
    {
        public const string RouteRegistrarsElementName = "route-registrars";
        public const string ControllerAssembliesElementName = "controller-assemblies";
        public const string IncludeDefaultRouteAttributeName = "includeDefaultRoute";

        public MvcSupportFacilityConfig(IConfiguration innerConfig)
        {
            if (innerConfig != null)
                Initialize(innerConfig);
        }

        private void Initialize(IConfiguration innerConfig)
        {
            UpdateIncludes(innerConfig);
            LoadControllerTypes(innerConfig);
            LoadRouteRegistrars(innerConfig);
        }

        private void UpdateIncludes(IConfiguration innerConfig)
        {
            bool include;
            var includeDefaultRouteFlag = innerConfig.Attributes[IncludeDefaultRouteAttributeName];
            if( Boolean.TryParse(includeDefaultRouteFlag, out include))
                includeDefaultRoute = include;
        }

        private void LoadControllerTypes(IConfiguration innerConfig)
        {
            var assemblyConfig = innerConfig.Children[ControllerAssembliesElementName];
            if (assemblyConfig != null)
                foreach (IConfiguration assemblyNode in assemblyConfig.Children)
                    LoadControllerTypesFromAssembly( AquireAssemblyByName(assemblyNode.Value) );
        }

        private void LoadControllerTypesFromAssembly(Assembly assembly)
        {
            foreach (var controllerType in assembly.GetExportedTypes())
                controllerTypes.Add(controllerType);
        }

        private static Assembly AquireAssemblyByName(string name)
        {
            return Assembly.Load(name);
        }

        private void LoadRouteRegistrars(IConfiguration innerConfig)
        {
            var routeRegistrarsFromConfig = innerConfig.Children[RouteRegistrarsElementName];
            if (routeRegistrarsFromConfig != null)
                foreach (IConfiguration registrarNode in routeRegistrarsFromConfig.Children)
                    routeRegistrars.Add(CreateRegistrarFromName(registrarNode.Value) );
        }

        private static IRouteRegistrar CreateRegistrarFromName(string registrarTypeName)
        {
            var registrar = CreateWithDefaultConstructorByName(registrarTypeName) as IRouteRegistrar;
            if (registrar == null)
                throw new InvalidOperationException("Type " + registrarTypeName + " does not implement the required IRouteRegistrar interface");
            return registrar;
        }

        private static object CreateWithDefaultConstructorByName(string name)
        {
            var type = Type.GetType(name);
            if (type == null)
                throw new InvalidOperationException("A Type cannot be found that matches " + name);

            var ctor = type.GetConstructor(new Type[] { });
            if (ctor == null)
                throw new InvalidOperationException("Type " + name + "must have a default constructor");

            return ctor.Invoke(new object[] { });
        }
    }
}
